package edu.xust.MetadataGenerator.service;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import edu.xust.MetadataGenerator.domain.API;
import edu.xust.MetadataGenerator.domain.APIParam;
import edu.xust.MetadataGenerator.domain.APIValidator;
import edu.xust.MetadataGenerator.domain.APIValidatorParam;
import edu.xust.MetadataGenerator.domain.Field;
import edu.xust.MetadataGenerator.domain.FieldConstraint;
import edu.xust.MetadataGenerator.domain.FieldValidator;
import edu.xust.MetadataGenerator.domain.FieldValidatorParam;
import edu.xust.MetadataGenerator.domain.MetaData;
import edu.xust.MetadataGenerator.domain.Table;
import edu.xust.MetadataGenerator.domain.TableConstraint;

public class MetaDataTreeHelper {
	private final static Logger logger = LogManager.getLogger(MetaDataTreeHelper.class);
	public static MetaData retrieveTreeData(JTree tree){
		MetaData metaData = null;
		TreeModel treeModel = tree.getModel();
		DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot();
		if(null != rootNode){
			Object rootData = rootNode.getUserObject();
			if("MetaData".equals(rootData.getClass().getSimpleName())){
				metaData = (MetaData)rootData;
				int childCount = rootNode.getChildCount();
				
				//Process tables
				List<Table> tables = new LinkedList<Table>();
				logger.debug("Output tables counts:" + childCount);
				for(int i = 0; i < childCount; i++){
					DefaultMutableTreeNode tablesNode = (DefaultMutableTreeNode) rootNode.getChildAt(i);
					Object tablesData = tablesNode.getUserObject();
					logger.debug(tablesData.getClass().getSimpleName() + ":");
					if(null != tablesData && MetaData.TABLES.equals(tablesData.toString())){
						int tablesChildCount = tablesNode.getChildCount();

						//Process table
						logger.debug("Output table counts:" + childCount);
						for(int j = 0; j < tablesChildCount; j++){
							DefaultMutableTreeNode tableNode = (DefaultMutableTreeNode) tablesNode.getChildAt(j);
							Table tableData = retrieveTableData(tableNode);
							
							tables.add(tableData);
						}
					}
				}
				metaData.setTables(tables);
			}
		}
		return metaData;
	}

	public static Table retrieveTableData(DefaultMutableTreeNode tableNode){
		Object tableData = tableNode.getUserObject();
		if(null != tableData && "Table".equals(tableData.getClass().getSimpleName())){
			int tableChildCount = tableNode.getChildCount();
			
			List<Field> fields = new LinkedList<Field>();
			List<TableConstraint> tableConstraints = new LinkedList<TableConstraint>();
			List<API> apis = new LinkedList<API>();
			((Table)tableData).setFields(fields);
			((Table)tableData).setConstraints(tableConstraints);
			((Table)tableData).setApis(apis);
			//Process table child
			for(int k = 0; k < tableChildCount; k++){
				DefaultMutableTreeNode tableChildNode = (DefaultMutableTreeNode) tableNode.getChildAt(k);
				Object tableChildData = tableChildNode.getUserObject();
				if(null != tableChildData){
					if(MetaData.FIELDS.equals(tableChildData.toString())){
						int fieldsCount = tableChildNode.getChildCount();
						
						//Process fields
						for(int l = 0; l < fieldsCount; l++){
							DefaultMutableTreeNode fieldNode = (DefaultMutableTreeNode) tableChildNode.getChildAt(l);
							Object fieldData = fieldNode.getUserObject();
							if(null != fieldData && "Field".equals(fieldData.getClass().getSimpleName())){
								List<FieldConstraint> fieldConstraints = new LinkedList<FieldConstraint>();
								List<FieldValidator> fieldValidators = new LinkedList<FieldValidator>();
								((Field)fieldData).setConstraints(fieldConstraints);
								((Field)fieldData).setValidators(fieldValidators);
								fields.add((Field)fieldData);
								int fieldChildCount = fieldNode.getChildCount();
								
								//Process field
								for(int m = 0; m < fieldChildCount; m++){
									DefaultMutableTreeNode fieldChildNode = (DefaultMutableTreeNode) fieldNode.getChildAt(m);
									Object fieldChildData = fieldChildNode.getUserObject();
									if(null != fieldChildData){		
										logger.debug("Constraint check");
										if(MetaData.FIELDCONSTRAINTS.equals(fieldChildData.toString())){
											//Process field constraints
											int fieldConstraintsCount = fieldChildNode.getChildCount();
											for(int n = 0; n < fieldConstraintsCount; n++){
												DefaultMutableTreeNode fieldConstraintNode = (DefaultMutableTreeNode) fieldChildNode.getChildAt(n);
												Object fieldConstraintData = fieldConstraintNode.getUserObject();
												if(null != fieldConstraintData){
													logger.debug("Constraints found!");
													fieldConstraints.add((FieldConstraint) fieldConstraintData);
												}
											}
										}else if(MetaData.FIELDVALIDATORS.equals(fieldChildData.toString())){
											//Process field validators
											int fieldValidatorCount = fieldChildNode.getChildCount();
											for(int o = 0; o < fieldValidatorCount; o++){
												DefaultMutableTreeNode fieldValidatorNode = (DefaultMutableTreeNode) fieldChildNode.getChildAt(o);
												Object fieldValidatorData = fieldValidatorNode.getUserObject();
												if(null != fieldValidatorData && "FieldValidator".equals(fieldValidatorData.getClass().getSimpleName())){
													logger.debug(((FieldValidator)fieldValidatorData).getMessageU());
													fieldValidators.add((FieldValidator)fieldValidatorData);
													
													//Process field validator params
													List<FieldValidatorParam> fieldValidatorParams = new LinkedList<FieldValidatorParam>();
													((FieldValidator)fieldValidatorData).setParams(fieldValidatorParams);
													int fieldValidatorParamsCount = fieldValidatorNode.getChildCount();
													for(int p = 0; p < fieldValidatorParamsCount; p++){
														DefaultMutableTreeNode fieldValidatorParamsNode = (DefaultMutableTreeNode) fieldValidatorNode.getChildAt(p);
														Object fieldValidatorParamsData = fieldValidatorParamsNode.getUserObject();
														if(null != fieldValidatorParamsData && MetaData.FIELDVALIDATORPARAMS.equals(fieldValidatorParamsData.toString())){
															
															//Process field validator param
															int fieldValidatorParamCount = fieldValidatorParamsNode.getChildCount();
															for(int q = 0; q < fieldValidatorParamCount; q++){
																DefaultMutableTreeNode fieldValidatorParamNode = (DefaultMutableTreeNode) fieldValidatorParamsNode.getChildAt(q);
																Object fieldValidatorParamData = fieldValidatorParamNode.getUserObject();
																if(null != fieldValidatorParamData && "FieldValidatorParam".equals(fieldValidatorParamData.getClass().getSimpleName())){
																	fieldValidatorParams.add((FieldValidatorParam)fieldValidatorParamData);
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
						
					}else if(MetaData.TABLECONSTRAINTS.equals(tableChildData.toString())){
						//Process table constraints
						int tableConstraintCount = tableChildNode.getChildCount();
						for(int r = 0; r < tableConstraintCount; r++){
							DefaultMutableTreeNode tableConstraintNode = (DefaultMutableTreeNode) tableChildNode.getChildAt(r);
							Object tableConstraintData = tableConstraintNode.getUserObject();
							if(null != tableConstraintData){
								tableConstraints.add((TableConstraint) tableConstraintData);
							}
						}
					}else if(MetaData.APIS.equals(tableChildData.toString())){
						//Process apis
						int apiCount = tableChildNode.getChildCount();
						for(int s = 0; s < apiCount; s++){
							DefaultMutableTreeNode apiNode = (DefaultMutableTreeNode) tableChildNode.getChildAt(s);
							Object apiData = apiNode.getUserObject();
							if(null != apiData && "API".equals(apiData.getClass().getSimpleName())){
								apis.add((API)apiData);
								
								//Process api params
								List<APIParam> apiParams = new LinkedList<APIParam>();
								((API)apiData).setParams(apiParams);
								int apiParamsCount = apiNode.getChildCount();
								logger.debug(apiParamsCount);
								for(int u = 0; u < apiParamsCount; u++){
									DefaultMutableTreeNode apiParamsNode = (DefaultMutableTreeNode) apiNode.getChildAt(u);
									Object apiParamsData = apiParamsNode.getUserObject();
									
									//Process api param
									logger.debug("begin to precess api's param..." + apiParamsData.toString() + "--" + MetaData.APIPARAMS);
									if(null != apiParamsData && MetaData.APIPARAMS.equals(apiParamsData.toString())){
										int apiParamCount = apiParamsNode.getChildCount();
										logger.debug("api's param count:" + apiParamCount);
										for(int v = 0; v < apiParamCount; v++){
											DefaultMutableTreeNode apiParamNode = (DefaultMutableTreeNode) apiParamsNode.getChildAt(v);
											Object apiParamData = apiParamNode.getUserObject();
											if(null != apiParamData && "APIParam".equals(apiParamData.getClass().getSimpleName())){
												apiParams.add((APIParam)apiParamData);
												
												//Process api param validators
												List<APIValidator> apiParamValidators = new LinkedList<APIValidator>();
												((APIParam)apiParamData).setValidators(apiParamValidators);
												int apiParamValidatorsCount = apiParamNode.getChildCount();
												for(int w = 0; w < apiParamValidatorsCount; w++){
													DefaultMutableTreeNode apiParamValidatorsNode = (DefaultMutableTreeNode) apiParamNode.getChildAt(w);
													Object apiParamValidatorsData = apiParamValidatorsNode.getUserObject();
													if(null != apiParamValidatorsData && MetaData.APIVALIDATORS.equals(apiParamValidatorsData.toString())){
														
														//Process api param validator
														int apiParamValidatorCount = apiParamValidatorsNode.getChildCount();
														for(int x = 0; x < apiParamValidatorCount; x++){
															DefaultMutableTreeNode apiParamValidatorNode = (DefaultMutableTreeNode) apiParamValidatorsNode.getChildAt(x);
															Object apiParamValidatorData = apiParamValidatorNode.getUserObject();
															if(null != apiParamValidatorData && "APIValidator".equals(apiParamValidatorData.getClass().getSimpleName())){
																apiParamValidators.add((APIValidator) apiParamValidatorData);
																
																//Process api param validator params
																List<APIValidatorParam> apiValidatorParams = new LinkedList<APIValidatorParam>();
																((APIValidator) apiParamValidatorData).setParams(apiValidatorParams);
																int apiParamValidatorParams = apiParamValidatorNode.getChildCount();
																for(int y = 0; y < apiParamValidatorParams; y++){
																	DefaultMutableTreeNode apiParamValidatorParamsNode = (DefaultMutableTreeNode) apiParamValidatorNode.getChildAt(y);
																	Object apiParamValidatorParamsData = apiParamValidatorParamsNode.getUserObject();
																	if(null != apiParamValidatorParamsData && MetaData.APIVALIDATORPARAMS.equals(apiParamValidatorParamsData.toString())){
																		//Process api param validator param
																		int apiValidatorParamCount = apiParamValidatorParamsNode.getChildCount();
																		for(int z = 0; z < apiValidatorParamCount; z++){
																			DefaultMutableTreeNode apiParamValidatorParamNode = (DefaultMutableTreeNode) apiParamValidatorParamsNode.getChildAt(z);
																			Object apiParamValidatorParamData = apiParamValidatorParamNode.getUserObject();
																			if(null != apiParamValidatorParamData && "APIValidatorParam".equals(apiParamValidatorParamData.getClass().getSimpleName())){
																				apiValidatorParams.add((APIValidatorParam) apiParamValidatorParamData);
																			}
																		}
																	}
																}
															}
														}
													}
												}
											}
										}
									}

								}
							}
						}
					}
				}
			}
		}
		return (Table)tableData;
	}
	
	public static void fillTree(final JTree tree, MetaData metaData){
		DefaultMutableTreeNode root = new DefaultMutableTreeNode();
		metaData2Tree(tree, root, metaData);
		
		//tree.setModel(new DefaultTreeModel(root));
		
	}
	
	public static void importTables(final JTree tree, MetaData metaData){
		TreeModel treeModel = tree.getModel();
		DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot();
		if(null != rootNode){
			DefaultMutableTreeNode tablesNode = (DefaultMutableTreeNode) rootNode.getChildAt(0);
			insertTables(tablesNode, metaData.getTalbes());
		}
	}
	
	private static void metaData2Tree(JTree tree, DefaultMutableTreeNode root, MetaData metaData){
		if(null != root){
			root.setUserObject(metaData);
			
			/*
			DefaultMutableTreeNode  groupid = new DefaultMutableTreeNode();
			groupid.setUserObject(metaData.getGroupid());
			root.add(groupid);
	
			DefaultMutableTreeNode  artifactid = new DefaultMutableTreeNode();
			artifactid.setUserObject(metaData.getArtifactid());
			root.add(artifactid);
	
			DefaultMutableTreeNode  coyright = new DefaultMutableTreeNode();
			coyright.setUserObject(metaData.getCoyright());
			root.add(coyright);
	
			DefaultMutableTreeNode  author = new DefaultMutableTreeNode();
			author.setUserObject(metaData.getAuthor());
			root.add(author);
	
			DefaultMutableTreeNode  version = new DefaultMutableTreeNode();
			version.setUserObject(metaData.getVersion());
			root.add(version);
	
			DefaultMutableTreeNode  database = new DefaultMutableTreeNode();
			database.setUserObject(metaData.getDatabase());
			root.add(database);
	
			DefaultMutableTreeNode  date = new DefaultMutableTreeNode();
			date.setUserObject(metaData.getDate());
			root.add(date);
			*/
			DefaultMutableTreeNode  tablesNode = new DefaultMutableTreeNode();
			tablesNode.setUserObject(MetaData.TABLES);
			root.add(tablesNode);
			
			logger.debug(metaData.getTalbes().size());
			logger.debug("begin to read tables...");
			insertTables(tablesNode, metaData.getTalbes());
			/*
			for(Table table : metaData.getTalbes()){
				DefaultMutableTreeNode tableNode = new DefaultMutableTreeNode();
				tableNode.setUserObject(table);
				tablesNode.add(tableNode);
				insertTable(tableNode, table);
			}
			*/
			
			logger.debug("Read tables is over!");
			tree.setModel(new DefaultTreeModel(root));	
		}
	}
	
	public static void insertTables(DefaultMutableTreeNode tablesNode, List<Table>tables){
		if(null != tablesNode && null != tables){
			for(Table table : tables){
				DefaultMutableTreeNode tableNode = new DefaultMutableTreeNode();
				tableNode.setUserObject(table);
				tablesNode.add(tableNode);
				logger.info("insertTable...");
				insertTable(tableNode, table);
			}
		}
	}
	
	public static void insertTable(DefaultMutableTreeNode tableNode, Table table){
		if(null != tableNode){
			/*
			DefaultMutableTreeNode  name = new DefaultMutableTreeNode();
			name.setUserObject(table.getName());
			tableNode.add(name);
	
			DefaultMutableTreeNode  moduleid = new DefaultMutableTreeNode();
			moduleid.setUserObject(table.getModuleid());
			tableNode.add(moduleid);
	
			DefaultMutableTreeNode  theme = new DefaultMutableTreeNode();
			theme.setUserObject(table.getTheme());
			tableNode.add(theme);
	
			DefaultMutableTreeNode  comment = new DefaultMutableTreeNode();
			comment.setUserObject(table.getComment());
			tableNode.add(comment);
				
			*/
			DefaultMutableTreeNode  fields = new DefaultMutableTreeNode();
			fields.setUserObject(MetaData.FIELDS);
			tableNode.add(fields);
			for(Field field : table.getFields()){
				DefaultMutableTreeNode fieldNode = new DefaultMutableTreeNode();
				fieldNode.setUserObject(field);
				fields.add(fieldNode);
				insertField(fieldNode, field);
			}

			DefaultMutableTreeNode  constraintsNode = new DefaultMutableTreeNode();
			constraintsNode.setUserObject(MetaData.TABLECONSTRAINTS);
			tableNode.add(constraintsNode);
			for(TableConstraint tableConstraint : table.getConstraints()){
				DefaultMutableTreeNode tableConstraintNode = new DefaultMutableTreeNode();
				tableConstraintNode.setUserObject(tableConstraint);
				constraintsNode.add(tableConstraintNode);
			}

			DefaultMutableTreeNode  apisNode = new DefaultMutableTreeNode();
			apisNode.setUserObject(MetaData.APIS);
			tableNode.add(apisNode);
			for(API api : table.getApis()){
				DefaultMutableTreeNode apiNode = new DefaultMutableTreeNode();
				apiNode.setUserObject(api);
				apisNode.add(apiNode);
				insertAPI(apiNode, api);
			}
		}
	}

	public static void insertAPI(DefaultMutableTreeNode apiNode, API api){
		if(null != apiNode){	
			/*
			DefaultMutableTreeNode  name = new DefaultMutableTreeNode();
			name.setUserObject(api.getName());
			apiNode.add(name);
	
			DefaultMutableTreeNode  doc = new DefaultMutableTreeNode();
			doc.setUserObject(api.getDoc());
			apiNode.add(doc);
			
			DefaultMutableTreeNode _return = new DefaultMutableTreeNode();
			_return.setUserObject(api.getReturn());
			apiNode.add(_return);
			*/
			DefaultMutableTreeNode  paramsNode = new DefaultMutableTreeNode();
			paramsNode.setUserObject(MetaData.APIPARAMS);
			apiNode.add(paramsNode);
			for(APIParam apiParam : api.getParams()){
				DefaultMutableTreeNode apiParamNode = new DefaultMutableTreeNode();
				apiParamNode.setUserObject(apiParam);
				paramsNode.add(apiParamNode);
				insertAPIParam(apiParamNode, apiParam);
			}
		}
	}

	public static void insertAPIParam(DefaultMutableTreeNode apiParamNode, APIParam apiParam){
		if(null != apiParamNode){
			/*
			DefaultMutableTreeNode  name = new DefaultMutableTreeNode();
			name.setUserObject(apiParam.getName());
			apiParamNode.add(name);
	
			DefaultMutableTreeNode  type = new DefaultMutableTreeNode();
			type.setUserObject(apiParam.getType());
			apiParamNode.add(type);

			*/
			DefaultMutableTreeNode  validatorsNode = new DefaultMutableTreeNode();
			validatorsNode.setUserObject(MetaData.APIVALIDATORS);
			apiParamNode.add(validatorsNode);
			for(APIValidator apiValidator : apiParam.getValidators()){
				DefaultMutableTreeNode apiValidatorNode = new DefaultMutableTreeNode();
				apiValidatorNode.setUserObject(apiValidator);
				validatorsNode.add(apiValidatorNode);
				insertAPIValidator(apiValidatorNode, apiValidator);
			}
		}
	}

	public static void insertAPIValidator(DefaultMutableTreeNode apiValidatorNode, APIValidator apiValidator){
		if(null != apiValidatorNode){
			/*
			DefaultMutableTreeNode  type = new DefaultMutableTreeNode();
			type.setUserObject(apiValidator.getType());
			apiValidatorNode.add(type);
			*/
			DefaultMutableTreeNode  paramsNode = new DefaultMutableTreeNode();
			paramsNode.setUserObject(MetaData.APIVALIDATORPARAMS);
			apiValidatorNode.add(paramsNode);			
			for(APIValidatorParam apiValidatorParam : apiValidator.getParams()){
				DefaultMutableTreeNode apiValidatorParamNode = new DefaultMutableTreeNode();
				apiValidatorParamNode.setUserObject(apiValidatorParam);
				paramsNode.add(apiValidatorParamNode);
				insertAPIValidatorParam(apiValidatorParamNode, apiValidatorParam);
			}
		}
	}

	public static void insertAPIValidatorParam(DefaultMutableTreeNode apiValidatorParamNode, APIValidatorParam apiValidatorParam){
		if(null != apiValidatorParamNode){		
			/*
			DefaultMutableTreeNode  name = new DefaultMutableTreeNode();
			name.setUserObject(apiValidatorParam.getName());
			apiValidatorParamNode.add(name);
	
			DefaultMutableTreeNode  value = new DefaultMutableTreeNode();
			value.setUserObject(apiValidatorParam.getValue());
			apiValidatorParamNode.add(value);

			DefaultMutableTreeNode  type = new DefaultMutableTreeNode();
			type.setUserObject(apiValidatorParam.getType());
			apiValidatorParamNode.add(type);
			*/
		}
	}

	public static void insertField(DefaultMutableTreeNode fieldNode, Field field){
		if(null != fieldNode){	
			/*
			DefaultMutableTreeNode  name = new DefaultMutableTreeNode();
			name.setUserObject(field.getName());
			fieldNode.add(name);
	
			DefaultMutableTreeNode  type = new DefaultMutableTreeNode();
			type.setUserObject(field.getType());
			fieldNode.add(type);
					
			*/
			// Add constrains node
			DefaultMutableTreeNode  contraintsNode = new DefaultMutableTreeNode();
			contraintsNode.setUserObject(MetaData.FIELDCONSTRAINTS);
			fieldNode.add(contraintsNode);
			// Add constraint nodes
			for(FieldConstraint fieldConstraint : field.getConstraints()){
				DefaultMutableTreeNode fieldConstraintNode = new DefaultMutableTreeNode();
				fieldConstraintNode.setUserObject(fieldConstraint);
				contraintsNode.add(fieldConstraintNode);
			}
			
			// Add vaidators node
			DefaultMutableTreeNode  validatorsNode = new DefaultMutableTreeNode();
			validatorsNode.setUserObject(MetaData.FIELDVALIDATORS);
			fieldNode.add(validatorsNode);
			//Add validator nodes
			for(FieldValidator fieldValidator : field.getValidators()){
				DefaultMutableTreeNode fieldValidatorNode = new DefaultMutableTreeNode();
				fieldValidatorNode.setUserObject(fieldValidator);
				validatorsNode.add(fieldValidatorNode);
				insertFieldValidator(fieldValidatorNode, fieldValidator);
			}
		}
	}

	public static void insertFieldValidator(DefaultMutableTreeNode fieldValidatorNode, FieldValidator fieldValidator){
		if(null != fieldValidatorNode){	
			/*
			DefaultMutableTreeNode  type = new DefaultMutableTreeNode();
			type.setUserObject(fieldValidator.getType());
			fieldValidatorNode.add(type);
	
			DefaultMutableTreeNode  message = new DefaultMutableTreeNode();
			message.setUserObject(fieldValidator.getMessage());
			fieldValidatorNode.add(message);

			*/
			// Add params node
			DefaultMutableTreeNode  paramsNode = new DefaultMutableTreeNode();
			paramsNode.setUserObject(MetaData.FIELDVALIDATORPARAMS);
			fieldValidatorNode.add(paramsNode);
			// Add param nodes
			for(FieldValidatorParam fieldValidatorParam : fieldValidator.getParams()){
				DefaultMutableTreeNode fieldValidatorParamNode = new DefaultMutableTreeNode();
				fieldValidatorParamNode.setUserObject(fieldValidatorParam);
				paramsNode.add(fieldValidatorParamNode);
				insertFieldValidatorParam(fieldValidatorParamNode, fieldValidatorParam);
			}
		}
	}
	
	public static void insertFieldValidatorParam(DefaultMutableTreeNode fieldValidatorParamNode, FieldValidatorParam fieldValidatorParam){
		if(null != fieldValidatorParamNode){	
			/*
			DefaultMutableTreeNode  name = new DefaultMutableTreeNode();
			name.setUserObject(fieldValidatorParam.getName());
			fieldValidatorParamNode.add(name);
	
			DefaultMutableTreeNode  value = new DefaultMutableTreeNode();
			value.setUserObject(fieldValidatorParam.getValue());
			fieldValidatorParamNode.add(value);

			DefaultMutableTreeNode  type = new DefaultMutableTreeNode();
			type.setUserObject(fieldValidatorParam.getType());
			fieldValidatorParamNode.add(type);
			*/
		}
	}

}
